using System;
using System.Text;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Web;

using Framework;

namespace SchemaMonitor
{
	//Collection Class (Customisable half)
	public partial class CPhotoHashList
	{
		#region Filters
		public CPhotoHashList Except(CPhotoHashList set)
		{
			if (set.Count * 2 > this.Count)
			{
				var temp = new CPhotoHashList(this.Count / 2);
				foreach (var i in this)
					if (!set.Has(i))
						temp.Add(i);
				return temp;
			}

			var list = new CPhotoHashList(this);
            lock (set)
                foreach (var i in set)
				    list.Remove(i);
			return list;
		}
		public CPhotoHashList InUse
		{
			get
			{
				var list = new CPhotoHashList();
				foreach (var i in this)
					if (i.InUse)
						list.Add(i);
				return list;
			}
		}
		private CPhotoHashList _unused;
		public CPhotoHashList Unused_
		{
			get
			{
				if (null == _unused)
					_unused = Unused;
				return _unused;
			}
		}
		public CPhotoHashList Unused
		{
			get
			{
				var list = new CPhotoHashList();
				foreach (var i in this)
					if (!i.InUse)
						list.Add(i);
				return list;
			}
        }
        private CPhotoHashList _caption;
        public CPhotoHashList Caption_
        {
            get
            {
                if (null == _caption)
                    _caption = Caption;
                return _caption;
            }
        }
        public CPhotoHashList Caption
        {
            get
            {
                var list = new CPhotoHashList();
                foreach (var i in this)
                    if (!string.IsNullOrEmpty(i.PhotoCaptions))
                        list.Add(i);
                return list;
            }
        }
        private CPhotoHashList _untagged;
		public CPhotoHashList Untagged_
		{
			get
			{
				if (null == _untagged)
					_untagged = Untagged;
				return _untagged;
			}
		}
		public CPhotoHashList Untagged
		{
			get
			{
				var list = new CPhotoHashList();
				foreach (var i in this)
					if (0 == i.TagCount)
						list.Add(i);
				return list;
			}
		}
		public CPhotoHashList Tagged
		{
			get
			{
				var list = new CPhotoHashList();
				foreach (var i in this)
					if (i.Tagged)
						list.Add(i);
				return list;
			}
		}

		public CPhotoHashList Delete { get { return CMetaTag.Cache.Delete.PhotoHashsO2O; } }
		public CPhotoHashList XRated { get { return CMetaTag.Cache.XRated.PhotoHashsO2O; } }
		public CPhotoHashList Laptop { get { return CMetaTag.Cache.Laptop.PhotoHashsO2O; } }
		#endregion

		#region Aggregation
		#endregion

		#region Searching (Optional)
		//Represents a simple search box to search PK and any string columns (add overloads as required, based on the pattern below)
		//e.g. public CPhotoHashList Search(string nameOrId, ) { ...
		public CPhotoHashList Search(string nameOrId)
        {
            //1. Normalisation
            nameOrId = (nameOrId??string.Empty).Trim().ToLower();
            
            //2. Start with a complete list
            CPhotoHashList results = this;
            
            //3. Use any available index, such as those generated for fk/bool columns
            //Normal Case - non-unique index (e.g. foreign key)
            //if (int.MinValue != foreignKey) results = results.GetByForeignKey(foreignKey);

            //Special case - unique index (e.g. primary key)
            /*
            if (!string.IsNullOrEmpty(nameOrId)) 
            {
                int id;
                if (int.TryParse(nameOrId, out id))
                {
                    CPhotoHash obj = this.GetById(id);
                    if (null != obj)
                    {
                        results = new CPhotoHashList(1);
                        results.Add(obj);
                        return results;
                    }
                }
            }
            */
            
            //4. Exit early if remaining (non-index) filters are blank
            if (string.IsNullOrEmpty(nameOrId)) return results; 
            
            //5. Manually search each record using custom match logic, building a shortlist
            CPhotoHashList shortList = new CPhotoHashList();
            foreach (CPhotoHash i in results)
                if (Match(nameOrId, i))
                    shortList.Add(i); 
            return shortList;
        }
        //Manual Searching e.g for string-based columns i.e. anything not indexed (add more params if required)
        private bool Match(string name, CPhotoHash obj)
        {
            if (!string.IsNullOrEmpty(name)) //Match any string column
            {
                return false;   //If filter is active, reject any items that dont match
            }
            return true;    //No active filters (should catch this in step #4)
        }
        #endregion

        #region Cloning
        public CPhotoHashList Clone(CDataSrc target) //, int parentId)
        {
            //No Transaction
            if (target is CDataSrcRemote)
                return Clone(target, null); //, parentId);

            //Transaction
            using (IDbConnection cn = target.Local.Connection())
            {
                IDbTransaction tx = cn.BeginTransaction();
                try
                {
                    CPhotoHashList clone = Clone(target, tx); //, parentId);
                    tx.Commit();
                    return clone;
                }
                catch
                {
                    tx.Rollback();
                    throw;
                }
            }
        }
        public CPhotoHashList Clone(CDataSrc target, IDbTransaction txOrNull) //, int parentId)
        {
            CPhotoHashList list = new CPhotoHashList(this.Count);
            foreach (CPhotoHash i in this)
                list.Add(i.Clone(target, txOrNull)); //, parentId));  *Child entities must reference the new parent
            return list;
        }
        #endregion
        
        #region Export to Csv
        //Web - Need to add a project reference to System.Web, or comment out these two methods
        public void ExportToCsv(HttpResponse response) { ExportToCsv(response, "PhotoHashs.csv"); }
        public void ExportToCsv(HttpResponse response, string fileName)
        {
            CDataSrc.ExportToCsv(response, fileName); //Standard response headers
            StreamWriter sw = new StreamWriter(response.OutputStream);
            ExportToCsv(sw);
            sw.Flush();
            response.End();
        }

        //Non-web
        public void ExportToCsv(string filePath)
        {
            StreamWriter sw = new StreamWriter(filePath);
            ExportToCsv(sw);
            sw.Close();
        }

        //Logic
        protected void ExportToCsv(StreamWriter sw)
        {
            string[] headings = new string[] {"PhotoMd5", "PhotoParentMD5", "PhotoBlob", "PhotoWidth", "PhotoHeight", "PhotoCreated"};
            CDataSrc.ExportToCsv(headings, sw);
            foreach (CPhotoHash i in this)
            {
                object[] data = new object[] {i.PhotoMd5, i.PhotoParentMD5, i.PhotoBlob, i.PhotoWidth, i.PhotoHeight, i.PhotoCreated};
                CDataSrc.ExportToCsv(data, sw);
            }
        }
		#endregion


		public CPhotoHashList SortByLeastTagsThenCreated()
		{
			var temp = new CPhotoHashList(this);
			temp.Sort(new CSortByLeastTagsThenCreated());
			return temp;
		}
		public CPhotoHashList SortByNearest()
		{
			var temp = new CPhotoHashList(this);
			temp.Sort(new CSortByNearest());
			return temp;
		}
		public CPhotoHashList SortByMinId()
		{
			var temp = new CPhotoHashList(this);
			temp.Sort(new CSortByMinId());
			return temp;
		}

		private class CSortByMinId : IComparer<CPhotoHash>
		{
			public int Compare(CPhotoHash x, CPhotoHash y)
			{
				return x.Photos.MinId.CompareTo(y.Photos.MinId);
			}
		}
		private class CSortByNearest : IComparer<CPhotoHash>
		{
			public int Compare(CPhotoHash x, CPhotoHash y)
			{
				int d = x.PhotoCreated.CompareTo(y.PhotoCreated);
				if (d != 0) return d;
				d = x.Photos.MinId.CompareTo(y.Photos.MinId);
				return d;
			}
		}
		private class CSortByLeastTagsThenCreated : IComparer<CPhotoHash>
		{
			public int Compare(CPhotoHash x, CPhotoHash y)
			{
				int d = x.TagCount.CompareTo(y.TagCount);
				if (d != 0) return d;
				d = x.PhotoCreated.CompareTo(y.PhotoCreated);
                if (d != 0) return d;
                d = x.Photos.MinId.CompareTo(y.Photos.MinId);
                return d;
			}
		}



		#region Main Index (on PhotoMd5)
		public bool Has(Guid md5) { return Index.ContainsKey(md5); }
		public bool Has(CPhotoHash p) { return Index.ContainsKey(p.PhotoMd5); }

		public CPhotoHash GetById(Guid photoMd5)
		{
			CPhotoHash c = null;
			Index.TryGetValue(photoMd5, out c);
			return c;
		}
		[NonSerialized]
		private Dictionary<Guid, CPhotoHash> _index;
		private Dictionary<Guid, CPhotoHash> Index
		{
			get
			{
				if (null != _index)
					if (_index.Count == this.Count)
						return _index;

				var temp = new Dictionary<Guid, CPhotoHash>(this.Count);
				foreach (CPhotoHash i in this)
					temp.Add(i.PhotoMd5, i);
				_index = temp;
				return _index;
			}
		}
		#endregion



		public new void Add(CPhotoHash item)
		{
			if (null != _index && !_index.ContainsKey(item.PhotoMd5))
				_index[item.PhotoMd5] = item;

			_indexByParentMD5 = null;
			_index = null;

			base.Add(item);
		}
		public new void Remove(CPhotoHash item)
		{
			if (null != _index && _index.ContainsKey(item.PhotoMd5))
				_index.Remove(item.PhotoMd5);

			_indexByParentMD5 = null;
			_index = null;

			base.Remove(item);
		}
	}
}
